<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>数据可视化与统计分析</title>
  <!-- Tailwind CSS v3 -->
  <script src="https://cdn.tailwindcss.com"></script>
  <!-- Font Awesome -->
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <!-- ECharts -->
  <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
  <!-- Chart.js -->
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>
  
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#3b82f6',
            secondary: '#64748b',
            accent: '#0ea5e9',
            neutral: '#f1f5f9',
            'neutral-dark': '#334155',
            success: '#10b981',
            warning: '#f59e0b',
            danger: '#ef4444',
          },
          fontFamily: {
            sans: ['Inter', 'system-ui', 'sans-serif'],
          },
          boxShadow: {
            'card': '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
            'card-hover': '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
          }
        }
      }
    }
  </script>
  <style type="text/tailwindcss">
    @layer utilities {
      .transition-all-300 {
        transition: all 300ms ease-in-out;
      }
      .card {
        @apply bg-white rounded-lg shadow-card p-6 transition-all-300;
      }
      .card:hover {
        @apply shadow-card-hover;
      }
      .btn {
        @apply px-4 py-2 rounded-md font-medium transition-all-300 focus:outline-none focus:ring-2 focus:ring-offset-2;
      }
      .btn-primary {
        @apply bg-primary text-white hover:bg-primary/90 focus:ring-primary/50;
      }
      .btn-secondary {
        @apply bg-secondary text-white hover:bg-secondary/90 focus:ring-secondary/50;
      }
      .btn-accent {
        @apply bg-accent text-white hover:bg-accent/90 focus:ring-accent/50;
      }
      .input {
        @apply w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary;
      }
      .label {
        @apply block text-sm font-medium text-gray-700 mb-1;
      }
      .stat-value {
        @apply text-xl font-bold text-primary;
      }
      .stat-label {
        @apply text-sm text-gray-500;
      }
    }
  </style>
</head>
<body class="bg-gray-50 min-h-screen">
  <header class="bg-white shadow-sm">
    <div class="container mx-auto px-4 py-6">
      <h1 class="text-3xl font-bold text-center text-gray-800">数据可视化与统计分析</h1>
      <p class="text-center text-gray-600 mt-2">输入或生成数据，查看多种图表展示和统计分析结果</p>
    </div>
  </header>

  <main class="container mx-auto px-4 py-8">
    <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
      <!-- 左侧：数据输入和生成区域 -->
      <div class="lg:col-span-1">
        <div class="card mb-6">
          <h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
            <i class="fa fa-keyboard-o mr-2 text-primary"></i>手动输入数据
          </h2>
          <div class="space-y-4">
            <div>
              <label for="manualData" class="label">数据序列（用逗号分隔）</label>
              <textarea id="manualData" rows="4" class="input" placeholder="例如：86.21,85.71,75.56,72.95,86.67,63.64,77.54,92.16,40.48,77.78,68.75,68.75,82.09,100,37.5"></textarea>
            </div>
            <button id="loadManualData" class="btn btn-primary w-full">
              <i class="fa fa-line-chart mr-2"></i>加载数据
            </button>
          </div>
        </div>

        <div class="card">
          <h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
            <i class="fa fa-random mr-2 text-primary"></i>随机生成数据
          </h2>
          <div class="space-y-4">
            <div class="grid grid-cols-2 gap-4">
              <div>
                <label for="minValue" class="label">最小值</label>
                <input type="number" id="minValue" class="input" value="0">
              </div>
              <div>
                <label for="maxValue" class="label">最大值</label>
                <input type="number" id="maxValue" class="input" value="100">
              </div>
            </div>
            <div>
              <label for="dataCount" class="label">数据点数量</label>
              <input type="number" id="dataCount" class="input" value="15" min="3" max="100">
            </div>
            <div>
              <label for="distributionType" class="label">分布类型</label>
              <select id="distributionType" class="input">
                <option value="uniform">均匀分布</option>
                <option value="normal" selected>正态分布</option>
                <option value="exponential">指数分布</option>
              </select>
            </div>
            <button id="generateRandomData" class="btn btn-accent w-full">
              <i class="fa fa-magic mr-2"></i>生成数据
            </button>
          </div>
        </div>
      </div>

      <!-- 中间和右侧：图表展示和统计分析 -->
      <div class="lg:col-span-2 space-y-6">
        <!-- 图表类型选择和操作 -->
        <div class="card">
          <div class="flex flex-wrap items-center justify-between gap-4">
            <h2 class="text-xl font-semibold text-gray-800 flex items-center">
              <i class="fa fa-area-chart mr-2 text-primary"></i>图表展示
            </h2>
            <div class="flex flex-wrap gap-2">
              <select id="chartType" class="input py-1">
                <option value="scatter">点分布图</option>
                <option value="line">折线图</option>
                <option value="bar">柱状图</option>
                <option value="histogram">直方图</option>
                <option value="boxplot">箱线图</option>
              </select>
              <button id="downloadChart" class="btn btn-secondary">
                <i class="fa fa-download mr-1"></i>导出
              </button>
            </div>
          </div>
          <div id="chartContainer" class="mt-4 h-80"></div>
        </div>

        <!-- 统计分析结果 -->
        <div class="card">
          <h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
            <i class="fa fa-calculator mr-2 text-primary"></i>统计分析结果
          </h2>
          
          <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
            <div class="stat-item">
              <p class="stat-label">样本数量 (n)</p>
              <p id="sampleCount" class="stat-value">-</p>
            </div>
            <div class="stat-item">
              <p class="stat-label">均值 (μ)</p>
              <p id="meanValue" class="stat-value">-</p>
            </div>
            <div class="stat-item">
              <p class="stat-label">标准差 (σ)</p>
              <p id="stdValue" class="stat-value">-</p>
            </div>
            <div class="stat-item">
              <p class="stat-label">中位数</p>
              <p id="medianValue" class="stat-value">-</p>
            </div>
            <div class="stat-item">
              <p class="stat-label">偏度</p>
              <p id="skewnessValue" class="stat-value">-</p>
            </div>
            <div class="stat-item">
              <p class="stat-label">峰度</p>
              <p id="kurtosisValue" class="stat-value">-</p>
            </div>
          </div>
          
          <div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-4">
            <div class="bg-neutral p-4 rounded-lg">
              <h3 class="font-medium text-gray-800 mb-2">正态性检验</h3>
              <div class="space-y-2">
                <div class="flex justify-between">
                  <span class="text-sm text-gray-600">Shapiro-Wilk 统计量:</span>
                  <span id="shapiroStat" class="font-medium">-</span>
                </div>
                <div class="flex justify-between">
                  <span class="text-sm text-gray-600">p值:</span>
                  <span id="shapiroPValue" class="font-medium">-</span>
                </div>
                <div class="flex justify-between">
                  <span class="text-sm text-gray-600">结论:</span>
                  <span id="normalityConclusion" class="font-medium">-</span>
                </div>
              </div>
            </div>
            
            <div class="bg-neutral p-4 rounded-lg">
              <h3 class="font-medium text-gray-800 mb-2">拟合优度 (R²)</h3>
              <div class="space-y-2">
                <div class="flex justify-between">
                  <span class="text-sm text-gray-600">线性拟合 R²:</span>
                  <span id="linearRSquared" class="font-medium">-</span>
                </div>
                <div class="flex justify-between">
                  <span class="text-sm text-gray-600">二次拟合 R²:</span>
                  <span id="quadraticRSquared" class="font-medium">-</span>
                </div>
                <div class="flex justify-between">
                  <span class="text-sm text-gray-600">最佳拟合:</span>
                  <span id="bestFitConclusion" class="font-medium">-</span>
                </div>
              </div>
            </div>
          </div>
          
          <div class="mt-6">
            <button id="downloadReport" class="btn btn-secondary">
              <i class="fa fa-file-text-o mr-2"></i>下载统计报告
            </button>
          </div>
        </div>
      </div>
    </div>
  </main>

  <footer class="bg-neutral-dark text-white py-6 mt-8">
    <div class="container mx-auto px-4 text-center">
      <p>数据可视化与统计分析工具 &copy; 2025</p>
      <p class="text-sm text-gray-400 mt-1">使用 HTML5, ECharts 和 Tailwind CSS 构建</p>
    </div>
  </footer>

  <script>
    // 全局变量
    let currentData = [];
    let chart = null;

    // DOM 元素
    const manualDataInput = document.getElementById('manualData');
    const loadManualDataBtn = document.getElementById('loadManualData');
    const minValueInput = document.getElementById('minValue');
    const maxValueInput = document.getElementById('maxValue');
    const dataCountInput = document.getElementById('dataCount');
    const distributionTypeSelect = document.getElementById('distributionType');
    const generateRandomDataBtn = document.getElementById('generateRandomData');
    const chartTypeSelect = document.getElementById('chartType');
    const refreshChartBtn = document.getElementById('refreshChart');
    const downloadChartBtn = document.getElementById('downloadChart');
    const downloadReportBtn = document.getElementById('downloadReport');

    // 统计结果显示元素
    const sampleCountEl = document.getElementById('sampleCount');
    const meanValueEl = document.getElementById('meanValue');
    const stdValueEl = document.getElementById('stdValue');
    const medianValueEl = document.getElementById('medianValue');
    const skewnessValueEl = document.getElementById('skewnessValue');
    const kurtosisValueEl = document.getElementById('kurtosisValue');
    const shapiroStatEl = document.getElementById('shapiroStat');
    const shapiroPValueEl = document.getElementById('shapiroPValue');
    const normalityConclusionEl = document.getElementById('normalityConclusion');
    const linearRSquaredEl = document.getElementById('linearRSquared');
    const quadraticRSquaredEl = document.getElementById('quadraticRSquared');
    const bestFitConclusionEl = document.getElementById('bestFitConclusion');

    // 初始化图表
    function initChart() {
      const chartContainer = document.getElementById('chartContainer');
      chart = echarts.init(chartContainer);
      
      // 响应窗口大小变化
      window.addEventListener('resize', () => {
        chart.resize();
      });
    }

    // 渲染图表
    function renderChart(data, chartType = 'scatter') {
      if (!chart) initChart();
      
      const xAxisData = Array.from({ length: data.length }, (_, i) => i + 1);
      let series = [];
      let option = {
        title: {
          text: getChartTitle(chartType),
          left: 'center',
          textStyle: {
            fontSize: 16,
            fontWeight: 'normal'
          }
        },
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'cross',
            crossStyle: {
              color: '#999'
            }
          }
        },
        grid: {
          left: '3%',
          right: '4%',
          bottom: '3%',
          containLabel: true
        },
        xAxis: [
          {
            type: 'category',
            data: xAxisData,
            axisPointer: {
              type: 'shadow'
            },
            name: '数据点'
          }
        ],
        yAxis: [
          {
            type: 'value',
            name: '数值'
          }
        ]
      };

      switch (chartType) {
        case 'scatter':
          series = [{
            name: '数据点',
            type: 'scatter',
            data: data.map((value, index) => [index + 1, value]),
            symbolSize: 8,
            itemStyle: {
              color: '#3b82f6'
            }
          }];
          break;
          
        case 'line':
          series = [{
            name: '数据趋势',
            type: 'line',
            data: data,
            smooth: true,
            symbol: 'circle',
            symbolSize: 6,
            lineStyle: {
              width: 2,
              color: '#3b82f6'
            },
            itemStyle: {
              color: '#3b82f6'
            }
          }];
          break;
          
        case 'bar':
          series = [{
            name: '数据值',
            type: 'bar',
            data: data,
            itemStyle: {
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: '#3b82f6' },
                { offset: 1, color: '#60a5fa' }
              ])
            }
          }];
          break;
          
        case 'histogram':
          // 计算直方图数据
          const min = Math.min(...data);
          const max = Math.max(...data);
          const binCount = Math.min(10, Math.ceil(Math.sqrt(data.length)));
          const binWidth = (max - min) / binCount;
          
          const bins = Array(binCount).fill(0);
          data.forEach(value => {
            const binIndex = Math.min(binCount - 1, Math.floor((value - min) / binWidth));
            bins[binIndex]++;
          });
          
          const binLabels = bins.map((_, i) => {
            const start = (min + i * binWidth).toFixed(1);
            const end = (min + (i + 1) * binWidth).toFixed(1);
            return `${start}-${end}`;
          });
          
          option.xAxis[0].data = binLabels;
          option.xAxis[0].name = '区间';
          option.yAxis[0].name = '频数';
          
          series = [{
            name: '频数',
            type: 'bar',
            data: bins,
            itemStyle: {
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: '#0ea5e9' },
                { offset: 1, color: '#38bdf8' }
              ])
            }
          }];
          break;
          
        case 'boxplot':
          // 计算箱线图数据
          const sortedData = [...data].sort((a, b) => a - b);
          const q1Index = Math.floor(sortedData.length * 0.25);
          const q2Index = Math.floor(sortedData.length * 0.5);
          const q3Index = Math.floor(sortedData.length * 0.75);
          
          const q1 = sortedData[q1Index];
          const median = sortedData[q2Index];
          const q3 = sortedData[q3Index];
          const iqr = q3 - q1;
          const lowerWhisker = Math.max(sortedData[0], q1 - 1.5 * iqr);
          const upperWhisker = Math.min(sortedData[sortedData.length - 1], q3 + 1.5 * iqr);
          
          series = [{
            name: '箱线图',
            type: 'boxplot',
            data: [[lowerWhisker, q1, median, q3, upperWhisker]],
            itemStyle: {
              color: '#3b82f6'
            },
            lineStyle: {
              color: '#1e40af'
            }
          }];
          
          option.xAxis[0].data = ['数据分布'];
          break;
      }

      option.series = series;
      chart.setOption(option, true);
    }

    // 获取图表标题
    function getChartTitle(chartType) {
      const titles = {
        'scatter': '数据点分布图',
        'line': '数据趋势折线图',
        'bar': '数据值柱状图',
        'histogram': '数据分布直方图',
        'boxplot': '数据分布箱线图'
      };
      return titles[chartType] || '数据可视化';
    }

    // 处理手动输入的数据
    function handleManualData() {
      const inputText = manualDataInput.value.trim();
      if (!inputText) {
        alert('请输入数据');
        return;
      }
      
      try {
        // 解析输入的数据
        const data = inputText.split(/[\s,\n]+/)
          .map(item => parseFloat(item.trim()))
          .filter(item => !isNaN(item));
        
        if (data.length === 0) {
          alert('未能解析有效的数据');
          return;
        }
        
        currentData = data;
        renderChart(currentData, chartTypeSelect.value);
        calculateStatistics(data);
        
        // 显示成功消息
        showToast('数据加载成功');
      } catch (error) {
        alert('数据格式错误，请检查输入');
        console.error('解析数据时出错:', error);
      }
    }

    // 生成随机数据
    function generateRandomData() {
      try {
        const min = parseFloat(minValueInput.value);
        const max = parseFloat(maxValueInput.value);
        const count = parseInt(dataCountInput.value);
        const distribution = distributionTypeSelect.value;
        
        if (isNaN(min) || isNaN(max) || isNaN(count) || count < 3) {
          alert('请输入有效的参数');
          return;
        }
        
        let data = [];
        
        switch (distribution) {
          case 'uniform':
            // 均匀分布
            for (let i = 0; i < count; i++) {
              data.push(min + Math.random() * (max - min));
            }
            break;
            
          case 'normal':
            // 正态分布 (Box-Muller 变换)
            const mean = (min + max) / 2;
            const std = (max - min) / 6; // 覆盖 99.7% 的数据
            
            for (let i = 0; i < count; i++) {
              let u1 = Math.random();
              let u2 = Math.random();
              let z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
              let value = mean + z0 * std;
              
              // 确保值在范围内
              if (value < min) value = min;
              if (value > max) value = max;
              
              data.push(value);
            }
            break;
            
          case 'exponential':
            // 指数分布
            const lambda = 1 / ((max - min) / 2); // 调整参数使分布在范围内
            
            for (let i = 0; i < count; i++) {
              let u = Math.random();
              let value = min - Math.log(u) / lambda;
              
              // 确保值在范围内
              if (value > max) value = max;
              
              data.push(value);
            }
            break;
        }
        
        currentData = data;
        renderChart(currentData, chartTypeSelect.value);
        calculateStatistics(data);
        
        // 将生成的数据显示在手动输入框中
        manualDataInput.value = data.map(v => v.toFixed(2)).join(', ');
        
        // 显示成功消息
        showToast('随机数据生成成功');
      } catch (error) {
        alert('生成数据时出错');
        console.error('生成随机数据时出错:', error);
      }
    }

    // 计算统计指标
    function calculateStatistics(data) {
      const n = data.length;
      
      // 样本数量
      sampleCountEl.textContent = n;
      
      // 均值
      const mean = data.reduce((sum, val) => sum + val, 0) / n;
      meanValueEl.textContent = mean.toFixed(4);
      
      // 标准差
      const squaredDiffs = data.map(val => Math.pow(val - mean, 2));
      const variance = squaredDiffs.reduce((sum, val) => sum + val, 0) / n;
      const std = Math.sqrt(variance);
      stdValueEl.textContent = std.toFixed(4);
      
      // 中位数
      const sortedData = [...data].sort((a, b) => a - b);
      let median;
      if (n % 2 === 0) {
        median = (sortedData[n / 2 - 1] + sortedData[n / 2]) / 2;
      } else {
        median = sortedData[Math.floor(n / 2)];
      }
      medianValueEl.textContent = median.toFixed(4);
      
      // 偏度
      const cubedDiffs = data.map(val => Math.pow(val - mean, 3));
      const skewnessNumerator = cubedDiffs.reduce((sum, val) => sum + val, 0) / n;
      const skewness = skewnessNumerator / Math.pow(std, 3);
      skewnessValueEl.textContent = skewness.toFixed(4);
      
      // 峰度
      const fourthDiffs = data.map(val => Math.pow(val - mean, 4));
      const kurtosisNumerator = fourthDiffs.reduce((sum, val) => sum + val, 0) / n;
      const kurtosis = kurtosisNumerator / Math.pow(std, 4) - 3; // 减去3得到 excess kurtosis
      kurtosisValueEl.textContent = kurtosis.toFixed(4);
      
      // 正态性检验 (简化版 Shapiro-Wilk 检验)
      const shapiroStat = calculateShapiroWilk(data);
      shapiroStatEl.textContent = shapiroStat.toFixed(4);
      
      // 简化的 p 值计算 (实际应用中应该使用更复杂的方法或查表)
      const pValue = approximateShapiroWPValue(shapiroStat, n);
      shapiroPValueEl.textContent = pValue.toFixed(4);
      
      // 正态性结论
      const alpha = 0.05;
      if (pValue > alpha) {
        normalityConclusionEl.textContent = `无法拒绝正态分布假设 (p = ${pValue.toFixed(4)} > ${alpha})`;
        normalityConclusionEl.className = 'font-medium text-success';
      } else {
        normalityConclusionEl.textContent = `拒绝正态分布假设 (p = ${pValue.toFixed(4)} ≤ ${alpha})`;
        normalityConclusionEl.className = 'font-medium text-danger';
      }
      
      // 拟合优度 R²
      const xValues = Array.from({ length: n }, (_, i) => i);
      
      // 线性拟合
      const linearFit = linearRegression(xValues, data);
      const linearRSquared = calculateRSquared(data, linearFit.predicted);
      linearRSquaredEl.textContent = linearRSquared.toFixed(4);
      
      // 二次拟合
      const quadraticFit = quadraticRegression(xValues, data);
      const quadraticRSquared = calculateRSquared(data, quadraticFit.predicted);
      quadraticRSquaredEl.textContent = quadraticRSquared.toFixed(4);
      
      // 最佳拟合结论
      if (linearRSquared > quadraticRSquared) {
        bestFitConclusionEl.textContent = `线性拟合 (R² = ${linearRSquared.toFixed(4)})`;
      } else {
        bestFitConclusionEl.textContent = `二次拟合 (R² = ${quadraticRSquared.toFixed(4)})`;
      }
    }

    // 简化版 Shapiro-Wilk 检验
    function calculateShapiroWilk(data) {
      const n = data.length;
      const sortedData = [...data].sort((a, b) => a - b);
      const mean = data.reduce((sum, val) => sum + val, 0) / n;
      
      // 计算分子
      let numerator = 0;
      for (let i = 0; i < n; i++) {
        // 使用近似的权重系数
        const weight = i < n / 2 ? 1 : -1;
        numerator += weight * sortedData[i];
      }
      numerator = Math.pow(numerator, 2);
      
      // 计算分母
      let denominator = 0;
      for (let i = 0; i < n; i++) {
        denominator += Math.pow(sortedData[i] - mean, 2);
      }
      
      return numerator / denominator;
    }

    // 近似计算 Shapiro-Wilk 检验的 p 值
    function approximateShapiroWPValue(stat, n) {
      // 这是一个非常简化的近似，实际应用中应该使用更准确的方法
      if (n < 3) return 1.0;
      
      // 对于正态分布的数据，Shapiro-Wilk 统计量接近 1
      // 这里使用一个简单的函数来近似 p 值
      const normalizedStat = (stat - 0.5) * 2; // 将统计量映射到 0-1 范围
      const pValue = Math.exp(-Math.pow((1 - normalizedStat) * n / 10, 2));
      
      return Math.min(Math.max(pValue, 0.001), 0.999);
    }

    // 线性回归
    function linearRegression(x, y) {
      const n = x.length;
      let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
      
      for (let i = 0; i < n; i++) {
        sumX += x[i];
        sumY += y[i];
        sumXY += x[i] * y[i];
        sumXX += x[i] * x[i];
      }
      
      const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
      const intercept = (sumY - slope * sumX) / n;
      
      const predicted = x.map(val => slope * val + intercept);
      
      return { slope, intercept, predicted };
    }

    // 二次回归
    function quadraticRegression(x, y) {
      const n = x.length;
      let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0, sumXXX = 0, sumXXXX = 0, sumXXY = 0;
      
      for (let i = 0; i < n; i++) {
        sumX += x[i];
        sumY += y[i];
        sumXY += x[i] * y[i];
        sumXX += x[i] * x[i];
        sumXXX += x[i] * x[i] * x[i];
        sumXXXX += x[i] * x[i] * x[i] * x[i];
        sumXXY += x[i] * x[i] * y[i];
      }
      
      // 解线性方程组:
      // n*a0 + sumX*a1 + sumXX*a2 = sumY
      // sumX*a0 + sumXX*a1 + sumXXX*a2 = sumXY
      // sumXX*a0 + sumXXX*a1 + sumXXXX*a2 = sumXXY
      
      const A = [
        [n, sumX, sumXX],
        [sumX, sumXX, sumXXX],
        [sumXX, sumXXX, sumXXXX]
      ];
      
      const B = [sumY, sumXY, sumXXY];
      
      // 使用 Cramer's 法则求解
      const detA = determinant(A);
      
      if (detA === 0) {
        // 矩阵奇异，返回线性回归结果
        const linear = linearRegression(x, y);
        return { a0: linear.intercept, a1: linear.slope, a2: 0, predicted: linear.predicted };
      }
      
      const A0 = [
        [B[0], A[0][1], A[0][2]],
        [B[1], A[1][1], A[1][2]],
        [B[2], A[2][1], A[2][2]]
      ];
      
      const A1 = [
        [A[0][0], B[0], A[0][2]],
        [A[1][0], B[1], A[1][2]],
        [A[2][0], B[2], A[2][2]]
      ];
      
      const A2 = [
        [A[0][0], A[0][1], B[0]],
        [A[1][0], A[1][1], B[1]],
        [A[2][0], A[2][1], B[2]]
      ];
      
      const a0 = determinant(A0) / detA;
      const a1 = determinant(A1) / detA;
      const a2 = determinant(A2) / detA;
      
      const predicted = x.map(val => a0 + a1 * val + a2 * val * val);
      
      return { a0, a1, a2, predicted };
    }

    // 计算矩阵的行列式
    function determinant(matrix) {
      if (matrix.length === 2) {
        return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
      }
      
      if (matrix.length === 3) {
        return matrix[0][0] * (matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1])
             - matrix[0][1] * (matrix[1][0] * matrix[2][2] - matrix[1][2] * matrix[2][0])
             + matrix[0][2] * (matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]);
      }
      
      return 0;
    }

    // 计算 R²
    function calculateRSquared(actual, predicted) {
      const meanActual = actual.reduce((sum, val) => sum + val, 0) / actual.length;
      
      const totalSumOfSquares = actual.reduce((sum, val) => sum + Math.pow(val - meanActual, 2), 0);
      const residualSumOfSquares = actual.reduce((sum, val, i) => sum + Math.pow(val - predicted[i], 2), 0);
      
      return 1 - (residualSumOfSquares / totalSumOfSquares);
    }

    // 下载图表为图片
    function downloadChart() {
      if (!chart || currentData.length === 0) {
        alert('没有可下载的图表');
        return;
      }
      
      const dataURL = chart.getDataURL({
        type: 'png',
        pixelRatio: 2,
        backgroundColor: '#fff'
      });
      
      const link = document.createElement('a');
      link.download = `数据图表_${new Date().toISOString().slice(0, 10)}.png`;
      link.href = dataURL;
      link.click();
    }

    // 下载统计报告
    function downloadReport() {
      if (currentData.length === 0) {
        alert('没有可下载的统计报告');
        return;
      }
      
      // 收集统计信息
      const stats = {
        '样本数量': sampleCountEl.textContent,
        '均值 (μ)': meanValueEl.textContent,
        '标准差 (σ)': stdValueEl.textContent,
        '中位数': medianValueEl.textContent,
        '偏度': skewnessValueEl.textContent,
        '峰度': kurtosisValueEl.textContent,
        'Shapiro-Wilk 统计量': shapiroStatEl.textContent,
        'p值': shapiroPValueEl.textContent,
        '正态性结论': normalityConclusionEl.textContent,
        '线性拟合 R²': linearRSquaredEl.textContent,
        '二次拟合 R²': quadraticRSquaredEl.textContent,
        '最佳拟合': bestFitConclusionEl.textContent
      };
      
      // 创建报告内容
      let reportContent = `数据统计分析报告\n`;
      reportContent += `生成时间: ${new Date().toLocaleString()}\n\n`;
      
      reportContent += `原始数据:\n`;
      reportContent += currentData.map(v => v.toFixed(4)).join(', ') + '\n\n';
      
      reportContent += `统计结果:\n`;
      for (const [key, value] of Object.entries(stats)) {
        reportContent += `${key}: ${value}\n`;
      }
      
      // 创建下载链接
      const blob = new Blob([reportContent], { type: 'text/plain' });
      const url = URL.createObjectURL(blob);
      
      const link = document.createElement('a');
      link.download = `统计报告_${new Date().toISOString().slice(0, 10)}.txt`;
      link.href = url;
      link.click();
      
      URL.revokeObjectURL(url);
    }

    // 显示提示消息
    function showToast(message) {
      // 创建提示元素
      const toast = document.createElement('div');
      toast.className = 'fixed bottom-4 right-4 bg-green-500 text-white px-4 py-2 rounded-md shadow-lg z-50 transition-opacity duration-300';
      toast.textContent = message;
      
      // 添加到页面
      document.body.appendChild(toast);
      
      // 淡出效果
      setTimeout(() => {
        toast.style.opacity = '0';
        setTimeout(() => {
          document.body.removeChild(toast);
        }, 300);
      }, 3000);
    }

    // 事件监听
    document.addEventListener('DOMContentLoaded', () => {
      // 初始化图表
      initChart();
      
      // 加载示例数据
      const exampleData = [86.21,85.71,75.56,72.95,86.67,63.64,77.54,92.16,40.48,77.78,68.75,68.75,82.09,100,37.5];
      manualDataInput.value = exampleData.join(', ');
      
      // 加载示例数据
      currentData = exampleData;
      renderChart(currentData, chartTypeSelect.value);
      calculateStatistics(currentData);
      
      // 绑定事件
      loadManualDataBtn.addEventListener('click', handleManualData);
      generateRandomDataBtn.addEventListener('click', generateRandomData);
      chartTypeSelect.addEventListener('change', () => {
        if (currentData.length > 0) {
          renderChart(currentData, chartTypeSelect.value);
        }
      });
      downloadChartBtn.addEventListener('click', downloadChart);
      downloadReportBtn.addEventListener('click', downloadReport);
    });
  </script>
</body>
</html>
